package com.yami.shop.service.impl;

import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.enums.StockBillType;
import com.yami.shop.bean.enums.TakeStockBillStatusEnum;
import com.yami.shop.bean.enums.TakeStockExceptionEnum;
import com.yami.shop.bean.enums.TakeStockProdStatusEnum;
import com.yami.shop.bean.event.StockBillChangeEvent;
import com.yami.shop.bean.model.Sku;
import com.yami.shop.bean.model.StockBillLog;
import com.yami.shop.bean.model.TakeStock;
import com.yami.shop.bean.model.TakeStockProd;
import com.yami.shop.bean.param.TakeStockParam;
import com.yami.shop.bean.vo.TakeStockVO;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.dao.TakeStockMapper;
import com.yami.shop.manager.impl.LangManager;
import com.yami.shop.service.ProductService;
import com.yami.shop.service.SkuService;
import com.yami.shop.service.TakeStockProdService;
import com.yami.shop.service.TakeStockService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Citrus
 * @date 2021-09-15 11:18:33
 */
@Service
public class TakeStockServiceImpl extends ServiceImpl<TakeStockMapper, TakeStock> implements TakeStockService {

    @Autowired
    private TakeStockMapper takeStockMapper;
    @Autowired
    private TakeStockProdService takeStockProdService;
    @Autowired
    private SkuService skuService;
    @Autowired
    private ProductService productService;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private LangManager langManager;

    @Override
    public IPage<TakeStockVO> pageByParams(PageParam<TakeStock> page, TakeStockParam takeStock) {
        return takeStockMapper.pageByParams(page, takeStock);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateTakeStock(TakeStock takeStock) {
        this.checkInfo(takeStock, false);
        updateById(takeStock);
        //更新账面库存
        List<TakeStockProd> changeProdList = new ArrayList<>();
        List<TakeStockProd> takeStockProdList = takeStock.getTakeStockProdList();
        List<Long> deleteIds = this.updateBookStock(takeStockProdList, changeProdList, takeStock.getTakeStockId());
        if (CollectionUtils.isNotEmpty(deleteIds)) {
            takeStockProdService.removeByIds(deleteIds);
        }
        return takeStockProdService.saveOrUpdateBatch(changeProdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean finishTakeStock(TakeStock takeStock) {
        this.checkInfo(takeStock, false);
        takeStock.setBillStatus(TakeStockBillStatusEnum.COMPLETED.value());
        updateById(takeStock);
        List<TakeStockProd> takeStockProdList = takeStock.getTakeStockProdList();
        List<TakeStockProd> changeProdList = new ArrayList<>();
        List<Long> deleteIds = this.updateBookStock(takeStockProdList, changeProdList, takeStock.getTakeStockId());
        if (CollectionUtils.isNotEmpty(deleteIds)) {
            takeStockProdService.removeByIds(deleteIds);
        }
        Map<Integer, List<Sku>> successTakeStockMap = new HashMap<>(16);
        this.completedCheck(changeProdList, successTakeStockMap);
        // 记录出入库信息
        // 盘盈记录
        StockBillLog profitStockBillLog = new StockBillLog();
        profitStockBillLog.setShopId(takeStock.getShopId());
        profitStockBillLog.setEmployeeId(takeStock.getMaker());
        profitStockBillLog.setMakerMobile(takeStock.getMakerMobile());
        profitStockBillLog.setStockBillType(StockBillType.PROFIT_STORAGE.value());
        profitStockBillLog.setSourceOrderNo(takeStock.getTakeStockNo());
        applicationContext.publishEvent(new StockBillChangeEvent(profitStockBillLog,
                successTakeStockMap.get(TakeStockProdStatusEnum.PROFIT.value())));
        // 盘亏记录
        StockBillLog lossStockBillLog = new StockBillLog();
        lossStockBillLog.setShopId(takeStock.getShopId());
        lossStockBillLog.setEmployeeId(takeStock.getMaker());
        lossStockBillLog.setMakerMobile(takeStock.getMakerMobile());
        lossStockBillLog.setStockBillType(StockBillType.LOSS_OUTBOUND.value());
        lossStockBillLog.setSourceOrderNo(takeStock.getTakeStockNo());
        applicationContext.publishEvent(new StockBillChangeEvent(lossStockBillLog,
                successTakeStockMap.get(TakeStockProdStatusEnum.LOSS.value())));

        boolean updateBatch = takeStockProdService.saveOrUpdateBatch(changeProdList);
        return updateBatch;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean voidedTakeStock(TakeStock takeStock) {
        this.checkInfo(takeStock, true);
        takeStock.setBillStatus(TakeStockBillStatusEnum.VOIDED.value());
        updateById(takeStock);
        List<TakeStockProd> takeStockProdList = takeStock.getTakeStockProdList();
        ArrayList<TakeStockProd> changeProdList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(takeStockProdList)) {
            List<Long> deleteIds = this.updateBookStock(takeStockProdList, changeProdList, takeStock.getTakeStockId());
            if (CollectionUtils.isNotEmpty(deleteIds)) {
                takeStockProdService.removeByIds(deleteIds);
            }
        }
        if (CollectionUtils.isEmpty(changeProdList)) {
            return true;
        } else {
            return takeStockProdService.saveOrUpdateBatch(changeProdList);
        }
    }

    @Override
    public List<TakeStockVO> listByParams(TakeStockParam takeStock) {
        List<TakeStockVO> takeStockList = takeStockMapper.listByParams(takeStock);
        langManager.getTakeStockVOLang(takeStockList);
        return takeStockList;
    }

    private void completedCheck(List<TakeStockProd> changeProdList, Map<Integer, List<Sku>> successTakeStockMap) {
        List<Long> skuIds = changeProdList.stream().map(TakeStockProd::getSkuId).collect(Collectors.toList());
        List<Sku> skus = skuService.listSkuBySKuIds(skuIds);
        if (CollectionUtils.isEmpty(skus)) {
            //商品规格不存在
            throw new YamiShopBindException("yami.shop.sku.is.null");
        }
        Map<Long, Sku> skuMap = skus.stream().collect(Collectors.toMap(Sku::getSkuId, s -> s));
        // 用于记录盘亏的商品信息
        List<Sku> lossProdList = new ArrayList<>();
        // 用于记录盘盈的商品信息
        List<Sku> profitProdList = new ArrayList<>();
        for (TakeStockProd prod : changeProdList) {
            Sku sku = null;
            sku = skuMap.get(prod.getSkuId());
            //盈亏以及异常判断
            if (Objects.isNull(sku) || sku.getIsDelete() == 1) {
                //sku删除，异常
                prod.setIoType(TakeStockProdStatusEnum.EXCEPTION.value());
                prod.setExceptionReason(TakeStockExceptionEnum.DELETE_PROD.value());
                continue;
            } else if (!prod.getStocks().equals(sku.getStocks())) {
                //库存不相等，异常
                prod.setIoType(TakeStockProdStatusEnum.EXCEPTION.value());
                prod.setExceptionReason(TakeStockExceptionEnum.PROD_CHANGE.value());
                continue;
            }
            Boolean flag = null;
            if (prod.getStocks() > prod.getTotalStock()) {
                //盘亏
                prod.setIoType(TakeStockProdStatusEnum.LOSS.value());
                flag = updateStock(prod, sku, flag);
                // 保存商品信息用于记录出库
                lossProdList.add(sku);
            } else if (prod.getStocks() < prod.getTotalStock()) {
                //盘盈
                prod.setIoType(TakeStockProdStatusEnum.PROFIT.value());
                flag = updateStock(prod, sku, flag);
                // 保存商品信息用于记录入库
                profitProdList.add(sku);
            } else if (prod.getStocks().equals(prod.getTotalStock())) {
                //盘平
                prod.setIoType(TakeStockProdStatusEnum.EQUAL.value());
            }
            if (Objects.nonNull(flag) && BooleanUtil.isFalse(flag)) {
                //库存更新失败
                throw new YamiShopBindException("yami.shop.stock.update.error");
            }
            productService.removeProdCacheByProdId(prod.getProdId());
            skuService.removeSkuCacheBySkuId(prod.getSkuId(), prod.getProdId());
        }
        successTakeStockMap.put(TakeStockProdStatusEnum.LOSS.value(), lossProdList);
        successTakeStockMap.put(TakeStockProdStatusEnum.PROFIT.value(), profitProdList);
    }

    private Boolean updateStock(TakeStockProd prod, Sku sku, Boolean flag) {
        flag = productService.updateProdAndSkuStock(sku.getProdId(), sku.getSkuId(), prod.getTotalStock() - prod.getStocks());
        sku.setChangeStock(prod.getTotalStock() - prod.getStocks());
        sku.setStocks(prod.getTotalStock());
        return flag;
    }


    private void checkInfo(TakeStock takeStock, boolean isVoid) {
        TakeStock dbTakeStock = takeStockMapper.selectById(takeStock.getTakeStockId());
        if (!Objects.equals(dbTakeStock.getShopId(), takeStock.getShopId())) {
            //当前盘点信息不属于你的店铺
            throw new YamiShopBindException("yami.take.stock.message.error");
        }
        if (Objects.equals(dbTakeStock.getBillStatus(), TakeStockBillStatusEnum.COMPLETED.value())
                || Objects.equals(dbTakeStock.getBillStatus(), TakeStockBillStatusEnum.VOIDED.value())) {
            //当前盘点任务已完成或已作废，请刷新页面
            throw new YamiShopBindException("yami.take.stock.status.error");
        }
        //不是废弃再进行判断
        if (!isVoid) {
            List<TakeStockProd> prods = takeStock.getTakeStockProdList();
            for (TakeStockProd prod : prods) {
                if (Objects.isNull(prod.getTotalStock())) {
                    //商品实盘库存不能为空
                    throw new YamiShopBindException("yami.shop.total.stock.not.null");
                }
            }
        }
    }

    /**
     * 更新账面库存，删除商品
     */
    private List<Long> updateBookStock(List<TakeStockProd> takeStockProdList, List<TakeStockProd> changeProdList, Long takeStockId) {
        List<TakeStockProd> dbProdList = takeStockProdService.list(new LambdaQueryWrapper<TakeStockProd>()
                .eq(TakeStockProd::getTakeStockId, takeStockId));
        List<Long> dbProdIds = dbProdList.stream().map(TakeStockProd::getTakeStockProdId).collect(Collectors.toList());

        List<Long> skuIds = takeStockProdList.stream().map(TakeStockProd::getSkuId).collect(Collectors.toList());
        Map<Long, Sku> skuMap = skuService.listSkuBySKuIds(skuIds).stream().collect(Collectors.toMap(Sku::getSkuId, s -> s));
        List<Long> deleteIds = new ArrayList<>();
        for (TakeStockProd prod : takeStockProdList) {
            if (!skuMap.containsKey(prod.getSkuId())) {
                continue;
            }
            //普通商品
            Sku sku = skuMap.get(prod.getSkuId());

            if (prod.getStocks() > prod.getTotalStock()) {
                //盘亏
                prod.setIoType(TakeStockProdStatusEnum.LOSS.value());
            } else if (prod.getStocks() < prod.getTotalStock()) {
                //盘盈
                prod.setIoType(TakeStockProdStatusEnum.PROFIT.value());
            } else if (prod.getStocks().equals(prod.getTotalStock())) {
                //盘平
                prod.setIoType(TakeStockProdStatusEnum.EQUAL.value());
            }
            if (prod.getTakeStockProdId() != null && dbProdIds.contains(prod.getTakeStockProdId())) {
                prod.setStocks(sku.getStocks());
                changeProdList.add(prod);
                deleteIds.add(prod.getTakeStockProdId());
                continue;
            }
            //新增且sku存在
            prod.setStocks(sku.getStocks());
            changeProdList.add(prod);
        }
        dbProdIds.removeAll(deleteIds);
        return dbProdIds;
    }
}
